﻿using Cassandra.Data.Linq;
using Cognitum.CassandraTableStorage.Entities;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Cognitum.CassandraTableStorage
{
    public class CassandraTableStorageDataSource
    {
        #region | Private Fields |

        private CassandraTableStorageServiceContext _tableStorageServiceContext;

        #endregion | Private Fields |

        #region | Constructors |

        public CassandraTableStorageDataSource(string connectionString, int replicationFactor)
        {
            InitServiceContext(connectionString, replicationFactor);
        }

        #endregion | Constructors |

        #region | Private Methods |

        private CassandraTableStorageServiceContext GetServiceContext(string connectionString, int replicationFactor)
        {
            return new CassandraTableStorageServiceContext(connectionString, replicationFactor);
        }

        private void InitServiceContext(string connectionString, int replicationFactor)
        {
            _tableStorageServiceContext = GetServiceContext(connectionString, replicationFactor);
        }

        #endregion | Private Methods |

        #region | Public Methods |

        #region | SessionStateEntity |

        public void Delete(session_state_entity itemToDelete)
        {
            _tableStorageServiceContext.SessionStates.Where(
                x =>
                    x.session_id == itemToDelete.session_id &&
                    x.application_name == itemToDelete.application_name)
                    .Delete()
                    .Execute();
        }

        public IEnumerable<session_state_entity> GetSessions(string sessionId, string applicationName)
        {
            return _tableStorageServiceContext.SessionStates.Where(
                x =>
                    x.session_id == sessionId &&
                    x.application_name == applicationName)
                    .Execute()
                    .ToList();
        }

        public IEnumerable<session_state_entity> GetSessions(string sessionId, string applicationName, int lockId)
        {
            return _tableStorageServiceContext.SessionStates.Where(
                x =>
                  x.session_id == sessionId &&
                  x.application_name == applicationName &&
                  x.lock_id == lockId)
                  .Execute().ToList();
        }

        public IEnumerable<session_state_entity> GetSessions(string sessionId, string applicationName, bool isLocked, DateTimeOffset expires)
        {
            return _tableStorageServiceContext.SessionStates.Where(
                x =>
                   x.session_id == sessionId &&
                   x.application_name == applicationName &&
                   x.locked == isLocked &&
                   x.expires > expires)
                   .Execute().ToList();
        }

        public void Insert(session_state_entity newItem)
        {
            _tableStorageServiceContext.SessionStates.Insert(newItem).Execute();
        }

        public void Update(session_state_entity itemToUpdate)
        {
            _tableStorageServiceContext.SessionStates.Insert(itemToUpdate).Execute();
        }

        #endregion | SessionStateEntity |

        #region | Memberships |

        public int GetNumberOfUsersOnline(DateTimeOffset compareTime, string applicationName)
        {
            return _tableStorageServiceContext.GetNumberOfUsersOnline(applicationName);
        }

        public void Delete(user_entity itemToDelete)
        {
            _tableStorageServiceContext.Users.Where<user_entity>(
                 x =>
                     x.partition_key == itemToDelete.partition_key &&
                     x.application_name == itemToDelete.application_name)
                     .Delete()
                     .Execute();

            _tableStorageServiceContext.UsersOnLine.Where<user_online_entity>(
                x =>
                    x.partition_key == itemToDelete.partition_key &&
                    x.application_name == itemToDelete.application_name)
                    .Delete()
                    .Execute();

            _tableStorageServiceContext.UsersActive.Where<user_active_entity>(
               x =>
                   x.partition_key == itemToDelete.partition_key &&
                   x.application_name == itemToDelete.application_name)
                   .Delete()
                   .Execute();
        }

        //Find Users by part of them username (usernameToMatch could be a substring of the username)
        public IEnumerable<user_entity> FindUsers(string usernameToMatch, string applicationName)
        {
            //TODO var aa = typeof(role_entity).Name;

            List<string> usernameListsFounded = new List<string>();
            foreach (var username in _tableStorageServiceContext.GetUsernamesFromUsers(applicationName))
            {
                if (username.GetValue<string>("username").Contains(usernameToMatch))
                    usernameListsFounded.Add(username.GetValue<string>("username"));
            }
            if (usernameListsFounded.Count == 0)
                return new List<user_entity>();

            return _tableStorageServiceContext.Users.Where(
                x =>
                          x.application_name == applicationName &&
                          usernameListsFounded.ToArray().Contains(x.username) &&
                          x.is_deleted == false)
                          .Execute().ToList();
        }

        public IEnumerable<user_online_entity> GetOnlineUsers(DateTimeOffset compareTime, string applicationName)
        {
            return _tableStorageServiceContext.UsersOnLine.Where(
                x =>
                          x.application_name == applicationName)
                          .Execute().ToList();
        }

        public IEnumerable<user_entity> GetUser(string partitionKey, string applicationName)
        {
            return _tableStorageServiceContext.Users.Where(
                x =>
                            x.partition_key == partitionKey &&
                            x.application_name == applicationName)
                            .Execute()
                            .ToList();
        }

        public IEnumerable<user_entity> GetUsers(string applicationName)
        {
            return _tableStorageServiceContext.Users.Where(x =>
                             x.application_name == applicationName &&
                             x.is_deleted == false)
                             .Execute().ToList();
        }

        public IEnumerable<user_entity> GetUsersByEmail(string emailToMatch, string applicationName)
        {
            return _tableStorageServiceContext.Users.Where(
                x =>
                           x.application_name == applicationName &&
                           x.email == emailToMatch &&
                           x.is_deleted == false)
                           .Execute().ToList();
        }

        public void Insert(user_entity newItem, int userIsOnlineTimeWindow, int userIsActiveTimeWindow)
        {
            _tableStorageServiceContext.Users.Insert(newItem).Execute();

            _tableStorageServiceContext.UsersOnLine.Insert(new user_online_entity()
            {
                application_name = newItem.application_name,
                last_activity_date = newItem.last_activity_date,
                partition_key = newItem.partition_key,
                username = newItem.username
            }).SetTTL(userIsOnlineTimeWindow)
            .Execute();

            _tableStorageServiceContext.UsersActive.Insert(new user_active_entity()
            {
                application_name = newItem.application_name,
                last_activity_date = newItem.last_activity_date,
                partition_key = newItem.partition_key,
                username = newItem.username
            }).SetTTL(userIsActiveTimeWindow)
            .Execute();
        }

        public void Update(user_entity itemToUpdate, int userIsOnlineTimeWindow)
        {
            _tableStorageServiceContext.Users.Insert(itemToUpdate).Execute();

            _tableStorageServiceContext.UsersOnLine.Insert(new user_online_entity()
            {
                application_name = itemToUpdate.application_name,
                last_activity_date = itemToUpdate.last_activity_date,
                partition_key = itemToUpdate.partition_key,
                username = itemToUpdate.username
            }).SetTTL(userIsOnlineTimeWindow)
            .Execute();
        }

        public void Update(user_entity itemToUpdate, int userIsOnlineTimeWindow, int userIsActiveTimeWindow)
        {
            Update(itemToUpdate, userIsOnlineTimeWindow);

            _tableStorageServiceContext.UsersActive.Insert(new user_active_entity()
            {
                application_name = itemToUpdate.application_name,
                last_activity_date = itemToUpdate.last_activity_date,
                partition_key = itemToUpdate.partition_key,
                username = itemToUpdate.username
            }).SetTTL(userIsActiveTimeWindow)
          .Execute();
        }

        public IEnumerable<user_entity> FindUsersByEmail(string emailToMatch, string applicationName)
        {
            List<string> usernameListsFounded = new List<string>();
            int placeOfMonkeyToMatch = emailToMatch.IndexOf("@");
            if (placeOfMonkeyToMatch != -1)
            {
                string userNameEmailToMatch = emailToMatch.Remove(placeOfMonkeyToMatch);
                string domainNameToMatch = emailToMatch.Remove(0, placeOfMonkeyToMatch + 1);
                foreach (var user in _tableStorageServiceContext.GetEmailsFromUsers(applicationName))
                {
                    int placeOfMonkey = user.GetValue<string>("email").IndexOf("@");
                    if (user.GetValue<string>("email")
                        .Remove(placeOfMonkey)
                        .Contains(userNameEmailToMatch)
                        &&
                        user.GetValue<string>("email")
                        .Remove(0, placeOfMonkey + 1) == domainNameToMatch
                        )
                    {
                        usernameListsFounded.Add(user.GetValue<string>("username"));
                    }
                }
            }
            else
            {
                foreach (var user in _tableStorageServiceContext.GetEmailsFromUsers(applicationName))
                {
                    if (user.GetValue<string>("email").Contains(emailToMatch))
                    {
                        usernameListsFounded.Add(user.GetValue<string>("username"));
                    }
                }
            }
            if (usernameListsFounded.Count == 0)
                return new List<user_entity>();
            return _tableStorageServiceContext.Users.Where(
                x =>
                          x.application_name == applicationName &&
                          usernameListsFounded.ToArray().Contains(x.username) &&
                          x.is_deleted == false)
                          .Execute().ToList();
        }

        public void Delete(user_online_entity itemToDelete)
        {
            _tableStorageServiceContext.UsersOnLine.Where<user_online_entity>(
                 x =>
                     x.partition_key == itemToDelete.partition_key &&
                     x.application_name == itemToDelete.application_name)
                     .Delete()
                     .Execute();
        }

        #endregion | Memberships |

        #region |  Roles |

        public void Insert(role_entity roleEntity)
        {
            _tableStorageServiceContext.Roles.Insert(roleEntity).Execute();
        }

        public void Delete(role_user_entity roleEntity)
        {
            _tableStorageServiceContext.UsersInRole.Where(
               x =>
                   x.user_name == roleEntity.user_name &&
                   x.application_name == roleEntity.application_name &&
                   x.role_name == roleEntity.role_name)
                   .Delete()
                   .Execute();
        }

        public void DeleteRole(string roleName, string applicationName)
        {
            _tableStorageServiceContext.Roles.Where<role_entity>(
                  x =>
                        x.role_name == roleName &&
                       x.application_name == applicationName)
                      .Delete()
                      .Execute();
        }

        public IEnumerable<role_user_entity> FindUsersInRole(string roleName, string applicationName, string userNameToMatch)
        {
            List<string> usernameListsFounded = new List<string>();

            foreach (var username in _tableStorageServiceContext.GetUsernamesFromRoles(applicationName, roleName))
            {
                if (username.GetValue<string>("user_name").Contains(userNameToMatch))
                    usernameListsFounded.Add(username.GetValue<string>("user_name"));
            }
            if (usernameListsFounded.Count() == 0)
                return new List<role_user_entity>();
            return _tableStorageServiceContext.UsersInRole.Where(
                x =>
                           x.role_name == roleName &&
                           x.application_name == applicationName &&
                           usernameListsFounded.ToArray().Contains(x.user_name))
                          .Execute()
                          .ToList();
        }

        public IEnumerable<role_entity> GetAllRoles(string applicationName)
        {
            return _tableStorageServiceContext.Roles.Where(
                x =>
                           x.application_name == applicationName)
                           .Execute()
                           .ToList();
        }

        public IEnumerable<role_user_entity> GetRolesForUser(string userName, string applicationName)
        {
            return _tableStorageServiceContext.UsersInRole.Where(
                x =>
                          x.user_name == userName &&
                          x.application_name == applicationName)
                          .Execute()
                          .ToList();
        }

        public IEnumerable<role_user_entity> GetUserInRole(string userName, string applicationName, string roleName)
        {
            return _tableStorageServiceContext.UsersInRole.Where(
                x =>
                    x.user_name == userName &&
                    x.application_name == applicationName &&
                    x.role_name == roleName)
                    .Execute()
                    .ToList();
        }

        public IEnumerable<role_user_entity> GetUsersInRole(string roleName, string applicationName)
        {
            return _tableStorageServiceContext.UsersInRole.Where(
                x =>
                          x.application_name == applicationName &&
                          x.role_name == roleName)
                          .Execute()
                          .ToList();
        }

        public void Insert(role_user_entity newItem)
        {
            _tableStorageServiceContext.UsersInRole.Insert(newItem).Execute();
        }

        public bool IsUserInRole(string userName, string applicationName, string roleName)
        {
            return _tableStorageServiceContext.UsersInRole.Where(
                x =>
                           x.user_name == userName &&
                           x.application_name == applicationName &&
                           x.role_name == roleName)
                          .Execute()
                          .Count() > 0;
        }

        public bool RoleExists(string roleName, string applicationName)
        {
            return _tableStorageServiceContext.Roles.Where(
                x =>
                     x.role_name == roleName &&
                     x.application_name == applicationName)
                    .Execute()
                    .ToList()
                    .Count() > 0;
        }

        #endregion |  Roles |

        #region | Profiles |

        public int GetNumberAllProfiles(string applicationName)
        {
            return _tableStorageServiceContext.GetNumberAllProfiles(applicationName);
        }

        public int GetNumberActiveProfiles(string applicationName)
        {
            return _tableStorageServiceContext.GetNumberOfActiveProfiles(applicationName);
        }

        public List<string> FindActiveProfilesByUserName(string usernameToMatch, string applicationName)
        {
            List<string> usernameActiveListsFounded = new List<string>();
            foreach (var username in _tableStorageServiceContext.GetUsernamesFromActiveUsers(applicationName))
            {
                if (username.GetValue<string>("username").Contains(usernameToMatch))
                    usernameActiveListsFounded.Add(username.GetValue<string>("username"));
            }
            return usernameActiveListsFounded;
        }

        public IEnumerable<user_entity> FindUserProfiles(string usernameToMatch, string applicationName)
        {
            List<string> usernameListsFounded = new List<string>();
            foreach (var username in _tableStorageServiceContext.GetUsernamesFromUsersProfiles(applicationName))
            {
                if (username.GetValue<string>("username").Contains(usernameToMatch))
                    usernameListsFounded.Add(username.GetValue<string>("username"));
            }
            if (usernameListsFounded.Count == 0)
                return new List<user_entity>();
            return _tableStorageServiceContext.Users.Where(
                x =>
                           x.application_name == applicationName &&
                           usernameListsFounded.ToArray().Contains(x.username) &&
                           x.is_profile_deleted == false)
                          .Execute().ToList();
        }

        public List<string> GetActiveProfiles(string applicationName)
        {
            return _tableStorageServiceContext.UsersActive.Where(
                x =>
                            x.application_name == applicationName)
                            .Execute().Select(x => x.username).ToList();
        }

        public IEnumerable<user_entity> GetAllProfiles(string applicationName)
        {
            return _tableStorageServiceContext.Users.Where(
                x =>
                             x.application_name == applicationName &&
                             x.is_profile_deleted == false)
                             .Execute().ToList();
        }

        public void Delete(user_active_entity itemToDelete)
        {
            _tableStorageServiceContext.UsersActive.Where<user_active_entity>(
                 x =>
                     x.partition_key == itemToDelete.partition_key &&
                     x.application_name == itemToDelete.application_name)
                     .Delete()
                     .Execute();
        }

        #endregion | Profiles |

        #endregion | Public Methods |
    }
}